import serial
from serial.tools import list_ports
import time
class SerialReceiver:
    """简化版串口数据接收器，实时接收并转换文本数据"""
    
    def __init__(self, port='COM8', baudrate=9600, timeout=1, encoding='utf-8', group_size=3):
        """初始化串口接收器
        
        Args:
            port: 串口号，如'COM8'
            baudrate: 波特率，默认9600
            timeout: 超时时间（秒），默认1秒
            encoding: 数据编码方式，支持'utf-8'和'gbk'
            group_size: 每组文本的长度，默认3
        """
        self.port = port
        self.baudrate = baudrate
        self.timeout = timeout
        self.encoding = encoding.lower()
        self.group_size = group_size
        self.serial = None
        self.is_running = False
        self.byte_buffer = bytearray()  # 字节缓冲区
        self.text_buffer = ""  # 文本缓冲区
        self.current_distance = None  # 当前距离

    def find_port(self):
        """查找指定串口是否存在"""
        try:
            ports = list(list_ports.comports())
            for p in ports:
                if p.device == self.port:
                    print(f"找到端口: {p.device} ({p.description})")
                    return True
            print(f"未找到端口 {self.port}")
            print("可用端口:", [p.device for p in ports])
            return False
        except Exception as e:
            print(f"扫描端口时出错: {e}")
            return False
            
    def open(self):
        """打开串口连接"""
        if self.serial and self.serial.is_open:
            print(f"串口 {self.port} 已打开")
            return True
            
        try:
            self.serial = serial.Serial(
                port=self.port,
                baudrate=self.baudrate,
                timeout=self.timeout,
                bytesize=serial.EIGHTBITS,
                parity=serial.PARITY_NONE,
                stopbits=serial.STOPBITS_ONE
            )
            print(f"成功打开串口 {self.port}，波特率 {self.baudrate}")
            return True
        except serial.SerialException as e:
            print(f"无法打开串口 {self.port}: {e}")
            return False
            
    def close(self):
        """关闭串口连接"""
        self.stop()
        if self.serial and self.serial.is_open:
            self.serial.close()
            print(f"已关闭串口 {self.port}")
            
        
    def start(self):
        """开始接收数据"""
        if not self.serial or not self.serial.is_open:
            if not self.open():
                return False
                
        if self.is_running:
            print("接收线程已在运行")
            return True
            
        self.is_running = True
        # 使用线程避免阻塞主线程
        from threading import Thread
        thread = Thread(target=self._receive_loop)
        thread.daemon = True
        thread.start()
        print("开始接收数据...")
        time.sleep(1)
        return True
        
    def stop(self):
        """停止接收数据"""
        self.is_running = False
            
    def _receive_loop(self):
        """内部接收循环"""
        while self.is_running:
            try:
                if self.serial.in_waiting > 0:
                    # 读取所有可用数据
                    data = self.serial.read(self.serial.in_waiting)
                    
                    # 转换为文本
                    text_data = self._bytes_to_text(data)
                    
                    # 按指定长度分组
                    groups = self._group_text(text_data)
                    self.current_distance = groups[-1] if groups else None
                    
                        
            except serial.SerialException as e:
                print(f"串口读取错误: {e}")
                self.is_running = False
            except Exception as e:
                print(f"接收循环异常: {e}")
                self.is_running = False
                
    def _bytes_to_text(self, bytes_data):
        """将字节流转换为文本，处理不完整的多字节字符"""
        self.byte_buffer.extend(bytes_data)
        byte_decode = bytearray()
        
        if self.encoding == "gbk":
            while self.byte_buffer:
                if self.byte_buffer[0] < 0x80:  # 单字节
                    byte_decode.append(self.byte_buffer.pop(0))
                else:  # 双字节
                    if len(self.byte_buffer) >= 2:
                        byte_decode.append(self.byte_buffer.pop(0))
                        byte_decode.append(self.byte_buffer.pop(0))
                    else:
                        break
        elif self.encoding == "utf-8":
            while self.byte_buffer:
                first = self.byte_buffer[0]
                
                # 1-4字节字符判断
                if first < 0x80:
                    size = 1
                elif first < 0xE0:
                    size = 2
                elif first < 0xF0:
                    size = 3
                else:
                    size = 4
                    
                if len(self.byte_buffer) >= size:
                    byte_decode.extend(self.byte_buffer[:size])
                    del self.byte_buffer[:size]
                else:
                    break
        else:
            try:
                return self.byte_buffer.decode(self.encoding)
            except:
                return f"Unsupported encoding: {self.encoding}"
                
        try:
            return byte_decode.decode(self.encoding)
        except:
            return f"Decode error: {byte_decode.hex()}"
            
    def _group_text(self, text):
        """将文本按指定长度分组"""
        self.text_buffer += text
        groups = []
        
        while len(self.text_buffer) >= self.group_size:
            groups.append(self.text_buffer[:self.group_size])
            self.text_buffer = self.text_buffer[self.group_size:]
            
        return groups
    
    def get_current_distance(self):
        """获取当前距离"""
        dist_list=[]
        count=0
        while self.current_distance is None: pass
        while self.current_distance is not None: 
            dist_list.append(int(self.current_distance)/10)
            count+=1
            if count>=10:
                break
        dis_avg=sum(dist_list)/len(dist_list)
        return dis_avg


# 使用示例
if __name__ == "__main__":
    # 创建接收器实例
    receiver = SerialReceiver(
        port="COM10",
        baudrate=9600,
        encoding="utf-8",
        group_size=4 # 每3个字符为一组
    )
    
    
    # 查找并打开串口
    if receiver.find_port():
        receiver.start()
        
        try:
            # 保持主线程运行
            print("按Ctrl+C停止接收...")
            while True:
                current_distance = receiver.get_current_distance()
                if current_distance is not None:
                    print("当前距离:", current_distance)
        except KeyboardInterrupt:
            receiver.close()
            print("程序已停止")